home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (C) 1994, Silicon Graphics, Inc.
- * All Rights Reserved.
- *
- * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
- * the contents of this file may not be disclosed to third parties, copied or
- * duplicated in any form, in whole or in part, without the prior written
- * permission of Silicon Graphics, Inc.
- *
- * RESTRICTED RIGHTS LEGEND:
- * Use, duplication or disclosure by the Government is subject to restrictions
- * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
- * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
- * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
- * rights reserved under the Copyright Laws of the United States.
- */
- #include "comm.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <stream.h>
- #include <sys/socket.h>
- #include <sys/time.h>
- #include <netdb.h>
- #include <errno.h>
- #include <signal.h>
- #include <unistd.h>
- #include <bstring.h>
- // ADDED 08/11/93:
- #include <CC/osfcn.h>
-
- #define STATUS_NOCONNECTION -1
- #define STATUS_READY 0
-
- static TeamInfo teamStatus[NUMTEAMS]; // team information
- static PlayerInfo playerInfo[MAXPLAYERS]; // player information
- static int playerAlive[MAXPLAYERS];// TRUE if player alive
- static Team playerFlag[MAXPLAYERS]; // which flag player has
-
- #define FS FIELDSIZE
- #define BR (1.5 * BASERADIUS)
- static float teamBasePos[NUMTEAMS][3] = {
- { -FS, -FS, FS },
- { FS, -FS, -FS },
- { -FS, FS, FS },
- { FS, FS, -FS } };
- static float teamBaseSafety[NUMTEAMS][3] = {
- { -FS-BR, -FS-BR, FS+BR },
- { FS+BR, -FS-BR, -FS-BR },
- { -FS-BR, FS+BR, FS+BR },
- { FS+BR, FS+BR, -FS-BR } };
-
- static int connectFd, // connection socket
- maxFd, // highest fd
- directFd[MAXPLAYERS], // stream sockets
- statusFd[MAXPLAYERS]; // state of connection
- static int currentConnection = -1;
-
- static void removePlayer(int);
- static void serverStop(int);
-
- static int isAny(InAddr id)
- {
- return (id.s_addr == INADDR_ANY);
- }
-
- static int lookupPlayer(NetId id)
- {
- for (int i = 0; i < MAXPLAYERS; i++)
- if (directFd[i] != -1 && NetId(playerInfo[i].id) == id)
- return i;
- return -1;
- }
-
- static int lookupPlayer(int fd)
- {
- if (fd == -1) return -1;
- for (int i = 0; i < MAXPLAYERS; i++)
- if (fd == directFd[i])
- return i;
- return -1;
- }
-
- static int dread(int fd, void* b, int l)
- {
- if (fd == -1) return 0;
- currentConnection = fd;
- int e = read(fd, b, l);
- if (e < 0 || (e == 0 && l > 0)) {
- int i;
- if ((i = lookupPlayer(currentConnection)) != -1) {
- if (e == 0 || errno == ECONNRESET || errno == EINTR || errno == EBADMSG)
- removePlayer(i);
- else if (errno != EAGAIN) {
- if (e < 0) perror("error on dread");
- else cerr << "zero length read\n";
- serverStop(1);
- }
- }
- }
- currentConnection = -1;
- return e;
- }
-
- static int dwrite(int fd, const void* b, int l)
- {
- if (fd == -1) return 0;
- currentConnection = fd;
- int e = write(fd, b, l);
- if (e < 0 || (e == 0 && l > 0)) {
- int i;
- if ((i = lookupPlayer(currentConnection)) != -1) {
- if (e == 0 || errno == ECONNRESET || errno == EINTR || errno == EPIPE)
- removePlayer(i);
- else if (errno != EAGAIN) {
- if (e < 0) perror("error on dwrite");
- else cerr << "zero length write\n";
- serverStop(1);
- }
- }
- }
- currentConnection = -1;
- return e;
- }
-
- static void resetFlag(int i)
- {
- teamStatus[i].state = (teamStatus[i].players == 0) ? FlagNoExist : FlagReady;
- teamStatus[i].owner = NetId();
- teamStatus[i].position[0] = teamBasePos[i][0];
- teamStatus[i].position[1] = teamBasePos[i][1];
- teamStatus[i].position[2] = teamBasePos[i][2];
- }
-
- static int defineWorld(void)
- {
- for (int i = 0; i < NUMTEAMS; i++) {
- teamStatus[i].team = Team(i);
- teamStatus[i].players = 0;
- teamStatus[i].won = 0;
- teamStatus[i].lost = 0;
- resetFlag(i);
- }
- for (i = 0; i < MAXPLAYERS; i++) {
- playerFlag[i] = NoTeam;
- playerAlive[i] = FALSE;
- }
- return 0;
- }
-
- static int serverStart(void)
- {
- struct sockaddr_in addr;
-
- addr.sin_family = AF_INET;
- addr.sin_port = SERVERPORT; // special 'port assigner' port
- addr.sin_addr.s_addr = INADDR_ANY;
-
- /* now initialize things with me as server */
- /* make connect server socket */
- connectFd = socket(AF_INET, SOCK_STREAM, 0);
- if (connectFd == -1) {
- perror("couldn't make connect socket");
- return -1;
- }
- if (bind(connectFd, (const void*)&addr, sizeof(addr)) == -1) {
- perror("couldn't bind connect socket");
- close(connectFd);
- return -1;
- }
- if (listen(connectFd, 10) == -1) {
- perror("couldn't make connect socket queue");
- close(connectFd);
- return -1;
- }
- maxFd = connectFd;
-
- for (int i = 0; i < MAXPLAYERS; i++) { // no direct connections
- directFd[i] = -1;
- statusFd[i] = STATUS_NOCONNECTION;
- }
-
- return 0;
- }
-
- static void serverStop(int exitCode)
- {
- close(connectFd);
- for (int i = 0; i < MAXPLAYERS; i++)
- if (directFd[i] != -1)
- close(directFd[i]);
- exit(exitCode);
- }
-
- static int acceptDirect(int allow)
- {
- // establish a new direct connection
- struct sockaddr_in addr;
- int fail = 0, fd, fd2, addr_len = sizeof(addr);
-
- fd = accept(connectFd, (void*)&addr, &addr_len);
- if (fd == -1) {
- perror("couldn't accept connect socket");
- return -1;
- }
-
- addr.sin_family = AF_INET;
- addr.sin_port = 0; // use any available port
- addr.sin_addr.s_addr = INADDR_ANY;
- addr_len = sizeof(addr);
- fd2 = socket(AF_INET, SOCK_STREAM, 0);
- if (fd2 == -1) fail = TRUE;
- if (!fail && bind(fd2, &addr, sizeof(addr)) == -1) fail = TRUE;
- if (!fail && getsockname(fd2, &addr, &addr_len) == -1) fail = TRUE;
- if (fail || !allow) addr.sin_port = 0; // error or too many players
-
- // send which port to use for connection
- dwrite(fd, (void*)&addr.sin_port, sizeof(addr.sin_port));
- close(fd);
- if (fail || !allow) return -1;
-
- if (listen(fd2, 1) == -1) {
- perror("making queue for direct connection");
- close(fd2);
- return -1;
- }
- addr_len = sizeof(addr);
- fd = accept(fd2, (void*)&addr, &addr_len);
- if (fd == -1) {
- perror("making direct socket");
- return -1;
- }
- close(fd2);
- return fd;
- }
-
- // get message
- static int recvPacket(int fd, ServerPacket& packet)
- {
- return (dread(fd, (void*)&packet, sizeof(packet)) == sizeof(packet));
- }
-
- // send message to every player
- static void broadcastPacket(ServerPacket& packet)
- {
- packet.length = sizeof(ServerPacket) - sizeof(InAddr) - sizeof(int);
- for (int i = 0; i < MAXPLAYERS; i++)
- if (statusFd[i] == STATUS_READY)
- dwrite(directFd[i], (void*)&packet, sizeof(packet));
- }
-
- // send message to one player
- static void sendPacket(int fd, ServerPacket& packet)
- {
- packet.length = sizeof(ServerPacket) - sizeof(InAddr) - sizeof(int);
- dwrite(fd, (void*)&packet, sizeof(packet));
- }
-
- // send flag update to every player
- static void sendTeamUpdate(Team team)
- {
- ServerPacket p;
- p.type = SwFlagUpdate;
- p.any.flagUpdate.info = teamStatus[int(team)];
- broadcastPacket(p);
- }
-
- // return whose base a point is in
- static Team whoseBase(float p[3])
- {
- float dx, dy, dz;
- for (int i = 0; i < NUMTEAMS; i++) {
- dx = p[0] - teamBasePos[i][0],
- dy = p[1] - teamBasePos[i][1],
- dz = p[2] - teamBasePos[i][2];
- if (dx * dx + dy * dy + dz * dz < BASERADIUS * BASERADIUS)
- return Team(i);
- }
- return NoTeam;
- }
-
- static void grabFlag(int player, Team flagTeam)
- {
- ServerPacket msg;
- SwFlagGrabbedMessage& grabmsg = msg.any.flagGrabbed;
-
- // player now has a flag
- playerFlag[player] = flagTeam;
-
- // set new team status
- teamStatus[int(flagTeam)].state = FlagOnShip;
- teamStatus[int(flagTeam)].owner = playerInfo[player].id;
-
- // tell everyone about grab
- msg.type = SwFlagGrabbed;
- grabmsg.grabber = playerInfo[player].id;
- grabmsg.info = teamStatus[int(flagTeam)];
- broadcastPacket(msg);
- }
-
- static void dropFlag(int player, Team flagTeam, float p[3])
- {
- Team b = whoseBase(p);
- ServerPacket msg;
- SwFlagDroppedMessage& dropmsg = msg.any.flagDropped;
-
- // player has no flag
- playerFlag[player] = NoTeam;
-
- // set new team status
- teamStatus[int(flagTeam)].state = FlagReady;
- teamStatus[int(flagTeam)].owner = NetId();
- if (b == NoTeam) {
- teamStatus[int(flagTeam)].position[0] = p[0];
- teamStatus[int(flagTeam)].position[1] = p[1];
- teamStatus[int(flagTeam)].position[2] = p[2];
- }
- else {
- // team A dropped team B's flag in team C's base -- move to safety position
- teamStatus[int(flagTeam)].position[0] = teamBaseSafety[int(flagTeam)][0];
- teamStatus[int(flagTeam)].position[1] = teamBaseSafety[int(flagTeam)][1];
- teamStatus[int(flagTeam)].position[2] = teamBaseSafety[int(flagTeam)][2];
- }
-
- // tell everyone about drop and new flag state
- msg.type = SwFlagDropped;
- dropmsg.dropper = playerInfo[player].id;
- dropmsg.info = teamStatus[int(flagTeam)];
- broadcastPacket(msg);
- }
-
- static void captureFlag(int player, Team flagTeam, Team captorTeam)
- {
- ServerPacket msg;
- SwFlagCapturedMessage &capturemsg = msg.any.flagCaptured;
-
- // update team scores
- teamStatus[int(flagTeam)].lost += teamStatus[int(flagTeam)].players;
- if (flagTeam != captorTeam)
- teamStatus[int(captorTeam)].won += teamStatus[int(flagTeam)].players;
-
- // put captured flag back in it's base (and whoever had it doesn't now)
- int p = lookupPlayer(teamStatus[flagTeam].owner);
- if (p != -1) playerFlag[p] = NoTeam;
- resetFlag(int(flagTeam));
-
- // tell everyone about drop and new flag state
- msg.type = SwFlagCaptured;
- capturemsg.captor = playerInfo[player].id;
- capturemsg.flagTeam = teamStatus[int(flagTeam)];
- capturemsg.captorTeam = teamStatus[int(captorTeam)];
- broadcastPacket(msg);
- }
-
- static void addPlayer(void)
- {
- int fd;
- ServerPacket msg;
-
- for (int i = 0; i < MAXPLAYERS; i++) // find open spot
- if (statusFd[i] == STATUS_NOCONNECTION)
- break;
-
- // establish a new direct connection
- fd = acceptDirect(i != MAXPLAYERS);
- if (fd == -1) return;
-
- // get join packet
- if (!recvPacket(fd, msg)) { // if can't get packet
- close(fd); // don't add player
- return;
- }
- if (msg.type != SwJoin) { // if not a join message
- close(fd); // reject connection
- return;
- }
-
- // set player information
- playerInfo[i] = msg.any.join.p; // store player data
- playerAlive[i] = FALSE; // start dead
- playerFlag[i] = NoTeam; // doesn't have a flag
-
- // record new connection
- directFd[i] = fd;
- if (directFd[i] > maxFd) maxFd = directFd[i]; // new max file descriptor
- statusFd[i] = MAXPLAYERS + NUMTEAMS; // I'm sending info
-
- // set team info. if first player on a team, add team's flag
- teamStatus[playerInfo[i].team].players++; // add a player to the team
- if (teamStatus[playerInfo[i].team].players == 1) {
- teamStatus[playerInfo[i].team].won = 0; // reset team's score
- teamStatus[playerInfo[i].team].lost = 0;
- resetFlag(playerInfo[i].team);
- sendTeamUpdate(playerInfo[i].team);
- }
-
- // tell everyone there's a new player
- msg.type = SwAddPlayer;
- msg.any.addPlayer.p = playerInfo[i];
- broadcastPacket(msg);
- }
-
- static void removePlayer(int player)
- {
- ServerPacket msg;
-
- // remove tank from file descriptor list
- close(directFd[player]);
- directFd[player] = -1;
- statusFd[player] = STATUS_NOCONNECTION;
-
- // tell everyone to remove player
- msg.type = SwRemovePlayer;
- msg.any.removePlayer.id = playerInfo[player].id;
- broadcastPacket(msg);
-
- // set team info. if last player on a team, remove team flag
- teamStatus[playerInfo[player].team].players--; // remove player from the team
- if (teamStatus[playerInfo[player].team].players == 0) {
-
- // determine if there are any more players left after this one is removed
- for (int i=0, teams_left=0; i<NUMTEAMS; i++)
- if (teamStatus[i].players != 0)
- teams_left++;
-
- // if there are no more teams, bring down the server
- if (!teams_left) {
- serverStop(0);
- }
- else {
- resetFlag(playerInfo[player].team);
- sendTeamUpdate(playerInfo[player].team);
- }
- }
-
- // check that player didn't have a flag
- if (playerFlag[player] != NoTeam && // hmmm, player had a flag
- teamStatus[int(playerFlag[player])].state != FlagNoExist) {
- resetFlag(playerFlag[player]);
- sendTeamUpdate(playerFlag[player]);
- }
-
- playerAlive[player] = FALSE;
- playerFlag[player] = NoTeam;
- }
-
- static void sendWorldInfo(int player)
- {
- int n = (MAXPLAYERS + NUMTEAMS) - statusFd[player];
- ServerPacket msg;
-
- msg.type = SwWorld;
- if (statusFd[player] == STATUS_NOCONNECTION) // this shouldn't happen
- return;
- if (statusFd[player] == STATUS_READY) { // say we're done with world
- msg.any.world.type = WorldNone;
- msg.any.world.count = 0;
- }
- if (n < NUMTEAMS) { // set team info
- msg.any.world.type = WorldTeam;
- msg.any.world.count = --statusFd[player];
- msg.any.world.info.team = teamStatus[n];
- }
- else { // set player info
- n -= NUMTEAMS;
- while (n < MAXPLAYERS && directFd[n] == -1) {
- statusFd[player]--;
- n++;
- }
- if (n < MAXPLAYERS) {
- msg.any.world.type = WorldPlayer;
- msg.any.world.count = --statusFd[player];
- msg.any.world.info.player = playerInfo[n];
- }
- else {
- msg.any.world.type = WorldNone;
- msg.any.world.count = 0;
- statusFd[player] = STATUS_READY;
- }
- }
- sendPacket(directFd[player], msg); // send info
- }
-
- static void handleMessage(int player, ServerPacket& msg)
- {
- if (player < 0 || player >= MAXPLAYERS) // invalid player number
- return;
- switch (msg.type) {
- case SwNone:
- // do nothing
- break;
- case SwJoin:
- // shouldn't get this. it's handled in addPlayer()
- break;
- case SwQuit:
- // remove player
- removePlayer(player);
- break;
- case SwGetWorld:
- // send more of current game state
- sendWorldInfo(player);
- break;
- case SwAlive:
- if (statusFd[player] == STATUS_READY)
- playerAlive[player] = TRUE;
- break;
- case SwGrabFlag:
- if (playerAlive[player] && playerFlag[player] == NoTeam &&
- teamStatus[msg.any.grabFlag.team].state == FlagReady)
- grabFlag(player, msg.any.grabFlag.team);
- break;
- case SwDropFlag:
- if (playerAlive[player] && playerFlag[player] == msg.any.dropFlag.team)
- dropFlag(player, msg.any.dropFlag.team, msg.any.dropFlag.position);
- break;
- case SwCaptureFlag:
- if (playerAlive[player] &&
- playerFlag[player] == msg.any.captureFlag.flagTeam)
- captureFlag(player, msg.any.captureFlag.flagTeam,
- msg.any.captureFlag.captorTeam);
- break;
- case SwKilled: {
- int victim = lookupPlayer(msg.any.killed.victim);
- Team killerTeam = playerInfo[lookupPlayer(msg.any.killed.killer)].team,
- victimTeam = playerInfo[victim].team;
- if (playerAlive[victim]) // note the dead guy
- playerAlive[victim] = FALSE;
- broadcastPacket(msg); // forward the message
- teamStatus[int(victimTeam)].lost++; // victim's team lost a point
- if (killerTeam == victimTeam) {
- teamStatus[int(killerTeam)].lost++; // make that two
- }
- else {
- teamStatus[int(killerTeam)].won++; // killer team wins a point
- sendTeamUpdate(killerTeam);
- }
- sendTeamUpdate(victimTeam);
- break;
- }
- case SwHit:
- // just forward the message
- broadcastPacket(msg);
- break;
- case SwMessage:
- // just forward the message
- broadcastPacket(msg);
- break;
- default:
- // shouldn't happen -- do nothing
- break;
- }
- }
-
- static void terminate_server(int ...)
- {
- serverStop(0);
- }
-
- static void usage(char* pname)
- {
- fprintf(stderr, "usage: %s\n", pname);
- exit(1);
- }
-
- static void parse(int argc, char** argv)
- {
- for (int i = 1; i < argc; i++)
- if (argv[i][0] == '-') switch(argv[i][1]) {
- default:
- usage(argv[0]);
- }
- else usage(argv[0]);
- }
-
- int main(int argc, char** argv)
- {
- fd_set dread_set;
-
- parse(argc, argv);
- if (defineWorld() < 0) return 1;
- if (serverStart() < 0) return 1;
-
- signal(SIGINT, terminate_server); // let user kill server
- signal(SIGTERM, terminate_server); // let user kill server
- signal(SIGPIPE, SIG_IGN); // don't die on SIGPIPE
-
- for (;;) { // loop forever
- // wait for a message on any of my connections
- FD_ZERO(&dread_set);
- for (int i = 0; i < MAXPLAYERS; i++)
- if (directFd[i] != -1)
- FD_SET(directFd[i], &dread_set);
- FD_SET(connectFd, &dread_set);
- int nfound = select(maxFd+1, (FD_TYPE*)&dread_set, NULL, NULL, NULL);
-
- if (nfound > 0) {
- if (FD_ISSET(connectFd, &dread_set)) // connection requested
- addPlayer();
- for (i = 0; i < MAXPLAYERS; i++) // look for message from player
- if (directFd[i] != -1 && FD_ISSET(directFd[i], &dread_set)) {
- ServerPacket msg;
- if (recvPacket(directFd[i], msg))
- handleMessage(i, msg);
- }
- }
- else if (nfound < 0) { // something went wrong
- perror("select error");
- if (errno != EINTR) sleep(1);
- }
- }
- }
-